package main

import (
	"encoding/json"
	"fmt"
	"log"
	"strconv"

	"github.com/hyperledger/fabric-contract-api-go/contractapi"
)

//fabric contractapi.Contract
type SimpleChaincode struct {
	contractapi.Contract
}

func (t *SimpleChaincode) PutValue(ctx contractapi.TransactionContextInterface, key string, value string) error {
	err := ctx.GetStub().PutState(key, []byte(value))
	fmt.Printf("put value success,key:%v,value:%v", key, value)
	return err
}

func (t *SimpleChaincode) GetValue(ctx contractapi.TransactionContextInterface, key string) (string, error) {
	b, err := ctx.GetStub().GetState(key)
	if b == nil {
		return "", fmt.Errorf("key doesn't exist")
	}
	return string(b), err
}

//初始化合约
func (t *SimpleChaincode) InitLedger(ctx contractapi.TransactionContextInterface) error {
	fmt.Println("方法:Init Ledger")
	return nil
}

//初始化合约数据
func (t *SimpleChaincode) InitLedgerData(ctx contractapi.TransactionContextInterface) error {
	fmt.Println("方法:Init Ledger Data")
	return initData(ctx)
}

//初始化合约数据
func (t *SimpleChaincode) DeleteLedgerData(ctx contractapi.TransactionContextInterface) error {
	fmt.Println("方法:DeleteLedgerData")
	return DeleteInitData(ctx)
}

//初始化CSC和SAC
func (t *SimpleChaincode) CreateCSCAndSAC(ctx contractapi.TransactionContextInterface) (map[string]interface{}, error) {
	fmt.Println("方法:CreateCsCAndSAC")
	miniTtustValue := 0.9
	deposit := 60.0
	maxNum := 3
	csc := CSCInit(miniTtustValue, deposit, maxNum)
	sensorList, err := t.QuerySensorList(ctx)
	if err != nil {
		return nil, err
	}
	for _, s := range sensorList {
		csc.SensorRegister(*s)
	}
	timestamp, _ := ctx.GetStub().GetTxTimestamp()
	timestampString := strconv.FormatInt(timestamp.AsTime().UnixMilli(), 10)
	CSCId := "CSC_" + timestampString
	SACId := "SAC_" + timestampString
	csc.Id = CSCId
	csc.SACId = SACId
	csc.MsgList = make([]string, 0)
	jsonByte, err := json.Marshal(csc)
	if err != nil {
		return nil, err
	}
	err = ctx.GetStub().PutState(csc.Id, jsonByte)
	if err != nil {
		return nil, err
	}
	//创建SAC
	maxNum = 3
	miniTokens := 100.0
	sac := SACInit(csc.Id, maxNum, miniTokens)
	sac.Id = SACId
	jsonByte, err = json.Marshal(sac)
	if err != nil {
		return nil, err
	}
	err = ctx.GetStub().PutState(sac.Id, jsonByte)
	fmt.Printf("csc: %v\n", csc)
	fmt.Printf("sac: %v\n", sac)
	result := make(map[string]interface{})
	result["csc"] = csc
	result["sac"] = sac
	return result, err
}

//CSC的sensor
func (t *SimpleChaincode) Fusion(ctx contractapi.TransactionContextInterface, cscId string) (*CSC, error) {
	//模拟sensor的消息融合的过程
	//查询csc
	csc, err := t.GetCSCById(ctx, cscId)
	if err != nil {
		return nil, err
	}
	//选一个空闲的PU
	PUList, err := t.QueryPUList(ctx)
	if err != nil {
		return nil, err
	}
	for _, p := range PUList {
		if p.Idle {
			csc.Result = p.Id
			//存回区块链
			jsonByte, err := json.Marshal(csc)
			if err != nil {
				return nil, err
			}
			err = ctx.GetStub().PutState(cscId, jsonByte)
			if err != nil {
				return nil, err
			}
			return csc, nil
		}
	}
	return nil, fmt.Errorf("no available PU")
}

//SAC的竞标提交
func (t *SimpleChaincode) Commit(ctx contractapi.TransactionContextInterface, sacId string, bidderId string) error {
	sac, err := t.GetSACById(ctx, sacId)
	if err != nil {
		return err
	}
	bidder, err := t.GetBidderById(ctx, bidderId)
	if err != nil {
		return err
	}
	result := sac.Commit(bidder)
	if !result {
		return fmt.Errorf(bidderId + " commit failed")
	}
	//存回区块链
	jsonByte, err := json.Marshal(sac)
	if err != nil {
		return err
	}
	//sac
	err = ctx.GetStub().PutState(sacId, jsonByte)
	if err != nil {
		return err
	}
	jsonByte, err = json.Marshal(bidder)
	if err != nil {
		return err
	}
	//bidder
	key, err := ctx.GetStub().CreateCompositeKey(BIDDER_PREFIX, []string{bidderId})
	if err != nil {
		return err
	}
	err = ctx.GetStub().PutState(key, jsonByte)
	return err
}

//投标揭示
func (t *SimpleChaincode) Reveal(ctx contractapi.TransactionContextInterface, sacId string) error {
	sac, err := t.GetSACById(ctx, sacId)
	if err != nil {
		return err
	}
	//找出最大的投标人
	winner := ""
	maxDeposit := 0.0
	for k, v := range sac.DpsMap {
		if maxDeposit < v {
			maxDeposit = v
			winner = k
		}
	}
	if maxDeposit == 0 {
		return fmt.Errorf("no bidder committed")
	}
	if winner != "" {
		//结算
		for k, v := range sac.DpsMap {
			if k == winner {
				//胜利者获得资源，sac余额增加
				sac.Refund = sac.Refund + v
				sac.Winner = k
			} else {
				//失败者退钱
				bidder, err := t.GetBidderById(ctx, k)
				if err != nil {
					return err
				}
				bidder.Deposit = bidder.Deposit + v
				key, err := ctx.GetStub().CreateCompositeKey(BIDDER_PREFIX, []string{k})
				if err != nil {
					return err
				}
				jsonByte, err := json.Marshal(bidder)
				if err != nil {
					return err
				}
				err = ctx.GetStub().PutState(key, jsonByte)
				if err != nil {
					return err
				}
			}
		}
	}
	//更新sac
	jsonByte, err := json.Marshal(sac)
	if err != nil {
		return err
	}
	err = ctx.GetStub().PutState(sacId, jsonByte)
	if err != nil {
		return err
	}
	//更新PU
	csc, err := t.GetCSCById(ctx, sac.CSCId)
	if err != nil {
		return err
	}
	if csc.Result != "" {
		pu, err := t.GetPUById(ctx, csc.Result)
		if err != nil {
			return err
		}
		pu.Idle = false
		jsonByte, err := json.Marshal(pu)
		if err != nil {
			return err
		}
		key, err := ctx.GetStub().CreateCompositeKey(PU_PREFIX, []string{pu.Id})
		if err != nil {
			return err
		}
		err = ctx.GetStub().PutState(key, jsonByte)
		if err != nil {
			return err
		}
	}
	//sensorList 信任值提升
	sensorList := csc.SensorList
	for _, sensor := range sensorList {
		sensor.TrustValue = sensor.TrustValue + 0.01
		if sensor.TrustValue > 100 {
			sensor.TrustValue = 100
		}
		jsonByte, err := json.Marshal(sensor)
		if err != nil {
			return err
		}
		key, err := ctx.GetStub().CreateCompositeKey(SENSOR_PREFIX, []string{sensor.Address})
		if err != nil {
			return err
		}
		err = ctx.GetStub().PutState(key, jsonByte)
		if err != nil {
			return err
		}
		fmt.Printf("update sensor success,key:%v,value:%v", key, sensor)
	}
	return nil
}

func main() {
	chaincode, err := contractapi.NewChaincode(&SimpleChaincode{})
	if err != nil {
		log.Panicf("Error creating asset chaincode: %v", err)
	}

	if err := chaincode.Start(); err != nil {
		log.Panicf("Error starting asset chaincode: %v", err)
	}
}
